using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Collections.ObjectModel;

namespace LibGreen.Jabber.ProtocolElements.IqElements
{
    /// <summary>
    /// An enumeration of valid &lt;iq&gt; element types
    /// </summary>
    enum IqType
    {
        /// <summary>
        /// The "get" type - a request for information or requirements
        /// </summary>
        Get,
        /// <summary>
        /// The "set" type - a command to set or replace required or optional data
        /// </summary>
        Set,
        /// <summary>
        /// The "error" type - a response to a failed "get" or "set"
        /// </summary>
        Error,
        /// <summary>
        /// The "result" type - a response to a successful "get" or "set"
        /// </summary>
        Result,
        /// <summary>
        /// An unknown type was sent or received
        /// </summary>
        Unknown,
    }

    /// <summary>
    /// The base class of all &lt;iq&gt; elements
    /// </summary>
    class IqElement
    {
        #region Static methods
        /// <summary>
        /// Converts a received type attribute to an <see cref="IqType"/> member
        /// </summary>
        /// <param name="iqtype">The received type attribute string</param>
        /// <returns>A corresponding <see cref="IqType"/> member</returns>
        private static IqType StringToIqType(string iqtype)
        {
            switch (iqtype.ToLowerInvariant())
            {
                case "get":
                    return IqType.Get;
                case "set":
                    return IqType.Set;
                case "error":
                    return IqType.Error;
                case "result":
                    return IqType.Result;
                default:
                    return IqType.Unknown;
            }
        }

        /// <summary>
        /// Converts the specified <see cref="IqType"/> member to a sendable type attribute
        /// </summary>
        /// <param name="iqtype">The requested <see cref="IqType"/> member</param>
        /// <returns>A sendable type attribute</returns>
        private static string IqTypeToString(IqType iqtype)
        {
            switch (iqtype)
            {
                case IqType.Error:
                    return "error";
                case IqType.Get:
                    return "get";
                case IqType.Result:
                    return "result";
                case IqType.Set:
                    return "set";
            }
            return "unknown";
        }
        #endregion

        /// <summary>
        /// Creates a new &lt;iq&gt; element originating from the client
        /// </summary>
        /// <param name="type">The <see cref="IqType"/> of the element</param>
        /// <returns>A new &lt;iq&gt; element</returns>
        public static Element Create(IqType type)
        {
            return Create(type, "", "");
        }

        /// <summary>
        /// Creates a new &lt;iq&gt; element originating from the client
        /// </summary>
        /// <param name="type">The <see cref="IqType"/> of the element</param>
        /// <param name="from">The resource that sent the element (may be blank or null)</param>
        /// <returns>A new &lt;iq&gt; element</returns>
        public static Element Create(IqType type, string from)
        {
            return Create(type, from, "");
        }

        /// <summary>
        /// Creates a new &lt;iq&gt; element originating from the client
        /// </summary>
        /// <param name="type">The <see cref="IqType"/> of the element</param>
        /// <param name="to">The resource to which to address the element</param>
        /// <param name="from">The resource that sent the element (may be blank or null)</param>
        /// <returns>A new &lt;iq&gt; element</returns>
        public static Element Create(IqType type, string from, string to)
        {
            Dictionary<string, string> attributes = new Dictionary<string, string>(3);
            attributes["type"] = IqTypeToString(type);
            if (!String.IsNullOrEmpty(from))
            {
                attributes["from"] = from;
            }
            if (!String.IsNullOrEmpty(to))
            {
                attributes["to"] = to;
            }
            return Element.CreateElement("iq", attributes);
        }

        private readonly IqType type;
        private readonly string id;
        private readonly string from;
        private readonly string to;
        private readonly Element originatingElement;
        private readonly ReadOnlyCollection<Element> children;

        /// <summary>
        /// Initializes an IqElement from a received <see cref="Element"/>
        /// </summary>
        /// <param name="element">The received <see cref="Element"/> containing the &lt;iq&gt; stanza</param>
        public IqElement(Element element)
        {
            // All IQ elements will have a type and an ID
            type = StringToIqType(element.Attributes["type"]);
            id = element.Attributes["id"];

            element.Attributes.TryGetValue("from", out from);
            element.Attributes.TryGetValue("to", out to);
            children = new ReadOnlyCollection<Element>(element.Children);
            originatingElement = element.OriginatingElement;
        }

        /// <summary>
        /// Gets the <see cref="IqType"/> of the element
        /// </summary>
        public IqType Type
        {
            get { return type; }
        }

        /// <summary>
        /// Gets the ID of the IQ element
        /// </summary>
        public string ID
        {
            get { return id; }
        }

        /// <summary>
        /// Gets a collection of the child elements in this stanza
        /// </summary>
        public ReadOnlyCollection<Element> Children
        {
            get { return children; }
        }

        /// <summary>
        /// Gets the <see cref="Element"/> that caused the creation of this element,
        /// or <c>null</c> if the current element originated from the client or unprompted from the server
        /// </summary>
        public Element OriginatingElement
        {
            get { return originatingElement; }
        }
    }
}
